module;
#include <iterator>
export module Code_20_3_1;


template<typename FROM, typename TO>
struct IsConvertibleHelper
{
private:
    static void aux(TO);
    template<typename F,
        typename = decltype(aux(std::declval<F>()))>
    static std::true_type test(void*);
    template<typename>
    static std::false_type test(...);

public:
    using Type = decltype(test<FROM>(nullptr));
};

template<typename FROM, typename TO>
struct IsConvertibleT : IsConvertibleHelper<FROM, TO>::Type
{
};

template<typename FROM, typename TO>
using IsConvertible = typename IsConvertibleT<FROM, TO>::Type;



template<bool, typename T = void>
struct EnableIfT {};

template< typename T>
struct EnableIfT<true, T>
{
    using Type = T;
};

template<bool Cond, typename T = void>
using EnableIf = typename EnableIfT<Cond, T>::Type;

template<typename Iterator>
constexpr bool IsRandomAccessIterator = IsConvertible< typename std::iterator_traits<Iterator>::iterator_category, std::random_access_iterator_tag>;


// implementation for random access iterators:
template<typename Iterator, typename Distance>
EnableIf<IsRandomAccessIterator<Iterator>> advanceIter(Iterator& x, Distance n) 
{
    x += n; // constant time
}
template<typename Iterator>
constexpr bool IsBidirectionalIterator =  IsConvertible< typename std::iterator_traits<Iterator>::iterator_category, std::bidirectional_iterator_tag>;


// implementation for bidirectional iterators:
template<typename Iterator, typename Distance>
EnableIf<IsBidirectionalIterator<Iterator> && !IsRandomAccessIterator<Iterator>> advanceIter(Iterator& x, Distance n) 
{
    if (n > 0) 
    {
        for (; n > 0; ++x, --n)  //linear time
        {
        }
    }
    else {
        for (; n < 0; --x, ++n) //linear time
        { 
        }
    }
}

// implementation for all other iterators:
template<typename Iterator, typename Distance>
EnableIf<!IsBidirectionalIterator<Iterator>> advanceIter(Iterator& x, Distance n) 
{
    if (n < 0) 
    {
        throw "advanceIter(): invalid iterator category for negativen";
    }
    while (n > 0)  //linear time
    { 
        ++x;
        --n;
    }
}

export
namespace Code_20_3_1 {

    void run()
    {

    }
}
